import logging
import queue
import re
import threading

from aiogram import types
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.types import InputFile, ParseMode

import client
import client_kb
import sqlite_db
from common_utils import paste_watermark
from create_bot import dp
from handlers.users.generate.est_country.est_country_menu import create_estonia

logger = logging.getLogger(__name__)


async def delete_user_reply(user_id: int, message_id: int):
    try:
        await dp.bot.delete_message(chat_id=user_id, message_id=message_id)
    except Exception as e:
        logger.warning(f"Unable to delete user message: {e}")


class FSM_EST_BILL(StatesGroup):
    bill_first_name = State()  # первое имя
    bill_last_name = State()  # последнее имя
    bill_father_name = State()  # отчество
    bill_edit_data = State()
    bill_street = State()  # улица
    bill_index = State()  # индекс
    bill_city = State()  # Насёленный пункт


back_kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
back_button = types.KeyboardButton(text="Назад ◀️")
back_button_inline = types.InlineKeyboardButton(
    text="Назад ◀️", callback_data="Назад ◀️"
)
back_kb.add(back_button)

data_fields = [
    {"title": "Имя", "key": "bill_first_name"},
    {"title": "Фамилия", "key": "bill_last_name"},
    {"title": "Отчество", "key": "bill_father_name"},
    {"title": "Адрес", "key": "bill_street"},
    {"title": "Индекс", "key": "bill_index"},
    {"title": "Населённый пункт", "key": "bill_city"},
]


async def data_checkup(user_id: int, state: FSMContext):
    try:
        data = await state.get_data()
        checkup_text = "Данные клиента:\n"
        for field in data_fields:
            if field["key"] in data:
                checkup_text += f"✅{field['title']}: {data[field['key']]}\n"
            else:
                checkup_text += f"{field['title']}:\n"
        if "message_to_edit_id" in data:
            await dp.bot.delete_message(
                chat_id=user_id, message_id=data["message_to_edit_id"]
            )
        return (await dp.bot.send_message(chat_id=user_id, text=checkup_text))[
            "message_id"
        ]
    except Exception as _ex:
        logger.exception(f"Ошибка в data_checkup: {_ex}")


async def bill_first_name_input(message: types.Message, state: FSMContext):
    logger.info(f"Пользователь {message.from_user.id} начал создание документа")
    await sqlite_db.sql_add_country_user(message.from_user.id, "est_bill")
    await state.update_data(type="bill")
    await dp.bot.send_photo(
        chat_id=message.from_user.id,
        photo=InputFile(f"./files/estonia_bill/example.png"),
    )
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите имя✏️\n", parse_mode=ParseMode.HTML, reply_markup=back_kb
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM_EST_BILL.bill_first_name.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM_EST_BILL.bill_first_name
)
async def bill_first_name_validate(message: types.Message, state: FSMContext):
    bill_first_name = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if re.match("^[A-Za-z `]+$", message.text) is not None:
        bill_first_name = message.text
    if bill_first_name is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите имя в правильном формате.\n"
                "<i>Формат ввода: только латинские буквы.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=back_kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл имя")
        await state.update_data(bill_first_name=bill_first_name.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await bill_last_name_input(message, state)


async def bill_last_name_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите фамилию✏️\n", parse_mode=ParseMode.HTML, reply_markup=back_kb
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM_EST_BILL.bill_last_name.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM_EST_BILL.bill_last_name
)
async def bill_last_name_validate(message: types.Message, state: FSMContext):
    bill_last_name = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if re.match("^[A-Za-z `]+$", message.text) is not None:
        bill_last_name = message.text
    if bill_last_name is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите фамилию в правильном формате.\n"
                "<i>Формат ввода: только латинские буквы.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=back_kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл фамилию")
        await state.update_data(bill_last_name=bill_last_name.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await bill_father_name_input(message, state)


async def bill_father_name_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
    kb.add(types.KeyboardButton("Пропустить")).add(back_button)
    question_message_id = (
        await message.answer(
            "Введите отчество✏️\n", parse_mode=ParseMode.HTML, reply_markup=kb
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM_EST_BILL.bill_father_name.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM_EST_BILL.bill_father_name
)
async def bill_father_name_validate(message: types.Message, state: FSMContext):
    bill_father_name = None
    msg = message.text
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()
    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")
    if re.match("^[A-Za-z `]+$", message.text) is not None:
        bill_father_name = message.text
    if message.text == "Пропустить":
        bill_father_name = " "
    if bill_father_name is None:
        kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
        kb.add(types.KeyboardButton("Пропустить")).add(back_button)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите отчество в правильном формате.\n"
                "<i>Формат ввода: только латинские буквы.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл отчество.")
        await state.update_data(bill_father_name=bill_father_name.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await bill_street_input(message, state)


async def bill_street_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите адрес проживания✏️\n"
            "Формат: <i>Улица проживания, номер дома</i>",
            parse_mode=ParseMode.HTML,
            reply_markup=back_kb,
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM_EST_BILL.bill_street.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM_EST_BILL.bill_street
)
async def bill_street_validate(message: types.Message, state: FSMContext):
    bill_street = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if message.text is not None:
        bill_street = message.text
    if bill_street is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите улицу в правильном формате.\n"
                "<i>Формат ввода: только латинские буквы.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=back_kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл улицу")
        await state.update_data(bill_street=bill_street.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await bill_index_input(message, state)


async def bill_index_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите индекс✏️\n", parse_mode=ParseMode.HTML, reply_markup=back_kb
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM_EST_BILL.bill_index.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM_EST_BILL.bill_index
)
async def bill_index_validate(message: types.Message, state: FSMContext):
    bill_index = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if re.match("^[-+]?[0-9]+$", message.text) is not None:
        bill_index = message.text
    if bill_index is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите индекс в правильном формате.\n"
                "<i>Формат ввода: только цифры.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=back_kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл индекс")
        await state.update_data(bill_index=bill_index.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await bill_city_input(message, state)


async def bill_city_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите город в котором живете✏️\n",
            parse_mode=ParseMode.HTML,
            reply_markup=back_kb,
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM_EST_BILL.bill_city.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM_EST_BILL.bill_city
)
async def bill_city_validate(message: types.Message, state: FSMContext):
    bill_city = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if re.match("^[A-Za-z `]+$", message.text) is not None:
        bill_city = message.text
    if bill_city is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите город в правильном формате.\n"
                "<i>Формат ввода: только латинские буквы.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=back_kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл город")
        await state.update_data(bill_city=bill_city.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await start_generate(message, state)


state_input_functions = {
    "FSM_EST_BILL:bill_first_name": create_estonia,
    "FSM_EST_BILL:bill_last_name": bill_first_name_input,
    "FSM_EST_BILL:bill_father_name": bill_last_name_input,
    "FSM_EST_BILL:bill_street": bill_father_name_input,
    "FSM_EST_BILL:bill_index": bill_street_input,
    "FSM_EST_BILL:bill_city": bill_index_input,
}


async def start_generate(message: types.Message, state: FSMContext):
    message_to_delete = await message.answer("Ожидайте генерируется...")
    data = await state.get_data()
    logger.info(f"Начинаем генерацию документа для пользователя {message.from_user.id}")
    result_queue = queue.Queue()
    id_thread = threading.Thread(
        target=generate_bill, args=(data, message.from_user.id, result_queue)
    )
    id_thread.start()
    id_thread.join()
    result = result_queue.get()
    if "Error" in result:
        logger.error(
            f"Ошибка при генерации документа для пользователя {message.from_user.id}:\n{result}"
        )
        await message.answer("❌Ошибка при генерации изображения")
        await dp.bot.send_message(chat_id=363661148, text=result)
    else:
        logger.info(
            f"Успешно завершили генерацию документа для пользователя {message.from_user.id}"
        )
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=message_to_delete["message_id"]
        )
        with open(result + "_wm.png", "rb") as photo:
            await dp.bot.send_document(chat_id=message.from_user.id, document=photo)
            await message.answer(
                "Завершите покупку документа.",
                reply_markup=client_kb.kb_est_finish_bill,
            )


@dp.message_handler(lambda message: message.text == "Назад ◀️", state=FSM_EST_BILL)
async def go_back(message: types.Message, state: FSMContext):
    current_state = str(await state.get_state())
    print(current_state)
    if current_state in state_input_functions:
        input_function = state_input_functions[current_state]
        await input_function(
            message, state
        )  # вызов функции ввода для предыдущего состояния
    else:
        await message.answer("Unknown state.")


@dp.callback_query_handler(lambda call: call.data == "Назад ◀️", state="*")
async def go_back(call: types.CallbackQuery, state: FSMContext):
    await call.message.delete()
    current_state = str(await state.get_state())
    print(current_state)
    if current_state in state_input_functions:
        input_function = state_input_functions[current_state]
        await input_function(
            call.message, state
        )  # вызов функции ввода для предыдущего состояния
    else:
        await call.message.answer("Unknown state.")


def generate_bill(data, user_id, result_queue):
    try:
        import calendar
        import datetime
        import random
        from datetime import datetime

        import codicefiscale as codicefiscale
        from dateutil.relativedelta import relativedelta
        from mrz.generator.td1 import TD1CodeGenerator
        from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont

        date_gave = datetime.now()
        date_expire = date_gave + relativedelta(days=15)

        last_month = date_gave - relativedelta(months=1)
        last_month_data = calendar.monthrange(
            int(last_month.year), int(last_month.strftime("%m"))
        )

        period = f"01-{last_month.strftime('%m')}-{last_month.year} / {last_month_data[1]}-{last_month.strftime('%m')}-{last_month.year}"
        combinate_date = f"18.{last_month.strftime('%m')}.{last_month.year}"

        background = Image.open("./files/estonia_bill/bill_shabl.jpg")

        draw = ImageDraw.Draw(background)
        font = ImageFont.truetype("./fonts/gillsansmt.ttf", 35)

        print(data)

        # specifying coordinates and colour of text
        draw.text(
            (200, 280),
            f"{str(data['bill_last_name']).capitalize()} {str(data['bill_first_name']).capitalize()} {str(data['bill_father_name']).capitalize()}",
            (0, 0, 0),
            font,
        )
        draw.text(
            (200, 320), f"{str(data['bill_street']).capitalize()}", (0, 0, 0), font
        )
        draw.text(
            (200, 360),
            f"{data['bill_index']} {str(data['bill_city']).capitalize()}",
            (0, 0, 0),
            font,
        )
        draw.text((200, 400), "Harjumaa", (0, 0, 0), font)

        font = ImageFont.truetype("./fonts/gillsansmt.ttf", 32)
        draw.text((1240, 545), str(date_gave.strftime("%d.%m.%Y")), (0, 0, 0), font)
        draw.text((1240, 575), str(date_expire.strftime("%d.%m.%Y")), (0, 0, 0), font)
        draw.text((1240, 608), "65725501725537165", (0, 0, 0), font)
        draw.text((1240, 638), "7014799", (0, 0, 0), font)
        draw.text((1240, 670), "1/1", (0, 0, 0), font)

        font = ImageFont.truetype("./fonts/gillsansmt.ttf", 28)
        draw.text((216, 908), period, (0, 0, 0), font)
        draw.text((505, 939), combinate_date, (0, 0, 0), font)

        kwT = int(random.randrange(1, 6))
        cost_KwT = f"{int(random.randrange(14, 16))}.{int(random.randrange(11, 99))}"
        summa = float(cost_KwT) * kwT

        precent = (summa / 100) * 20
        from math import ceil

        precent = ceil(precent * 100) / 100.0

        all_cost = float(int(summa) + float(precent))

        draw.text((430, 939), str(random.randrange(11111, 99999)), (0, 0, 0), font)
        draw.text((120, 972), "Vina tekok 10", (0, 0, 0), font)
        draw.text((780, 908), f"{kwT},00 TM", (0, 0, 0), font)
        draw.text((1005, 908), cost_KwT, (0, 0, 0), font)
        draw.text((1305, 908), str(float(summa))[0:5], (0, 0, 0), font)
        draw.text((1505, 908), str(float(all_cost))[0:5], (0, 0, 0), font)
        draw.text((1110, 1098), cost_KwT, (0, 0, 0), font)
        draw.text((1320, 1098), str(float(precent)), (0, 0, 0), font)
        draw.text((1505, 1098), str(float(all_cost))[0:5], (0, 0, 0), font)

        font = ImageFont.truetype("./fonts/GILB____.ttf", 29)
        draw.text((1500, 1146), str(float(all_cost))[0:5], (0, 0, 0), font)
        draw.text((1320, 1146), str(float(precent)), (0, 0, 0), font)

        font = ImageFont.truetype("./fonts/GILB____.ttf", 34)
        draw.text(
            (1240, 498), f"{random.randrange(111111111,999999999)}", (0, 0, 0), font
        )

        # saving the image
        image_name = f"./files/estonia_bill/photo_usr/{user_id}_completed"
        background.save(image_name + ".png")

        paste_watermark(
            f"./files/estonia_bill/photo_usr/{user_id}_completed.png",
            f"./files/estonia_bill/photo_usr/{user_id}_completed_wm.png",
        )
        result_queue.put(image_name)

    except:
        pass


async def start_generate_edit(user_id: int, state: FSMContext):
    message_to_delete = await dp.bot.send_message(
        chat_id=user_id, text="Ожидайте генерируется..."
    )
    await dp.bot.send_chat_action(user_id, "upload_photo")
    await state.update_data(progress_message_id=message_to_delete["message_id"])
    data = await state.get_data()
    result_queue = queue.Queue()
    id_thread = threading.Thread(
        target=generate_bill, args=(data, user_id, result_queue)
    )
    id_thread.start()
    await dp.bot.send_chat_action(user_id, "upload_photo")
    id_thread.join()
    result = result_queue.get()
    if "Error" in result:
        await dp.bot.send_message(
            chat_id=user_id, text="❌Ошибка при генерации изображения"
        )
    else:
        await dp.bot.delete_message(
            chat_id=user_id, message_id=message_to_delete["message_id"]
        )
        with open(result + "_wm.png", "rb") as photo:
            await dp.bot.send_document(chat_id=user_id, document=photo)
            await dp.bot.send_message(
                chat_id=user_id,
                text="Завершите покупку документа.",
                reply_markup=client_kb.kb_est_finish_bill,
            )


@dp.callback_query_handler(text="est_change_bill", state="*")
async def br_change_dl(call: types.CallbackQuery, state: FSMContext):
    kbS = types.ReplyKeyboardMarkup(one_time_keyboard=True)
    await call.message.delete()
    for field in data_fields:
        kbS.add(types.KeyboardButton(text=f'{field["title"]}'))
    edit_message_to_delete_id = (
        await call.message.answer(
            text="Выберите параметр, который хотите изменить", reply_markup=kbS
        )
    )["message_id"]
    await state.update_data(edit_message_to_delete_id=edit_message_to_delete_id)
    await FSM_EST_BILL.bill_edit_data.set()


@dp.callback_query_handler(text="est_pay_bill", state="*")
async def command_pay_it_bill(callback: types.CallbackQuery, state: FSMContext):
    await state.finish()
    await state.update_data(pay_country="est_bill")
    await client.command_promo_quest(callback, state)


@dp.message_handler(state=FSM_EST_BILL.bill_edit_data)
async def edit_field(message: types.Message, state: FSMContext):
    data = await state.get_data()
    await dp.bot.delete_message(
        chat_id=message.from_user.id, message_id=int(data["edit_message_to_delete_id"])
    )
    await message.delete()
    for field in data_fields:
        if field["title"] == message.text:
            key = field["key"]
    await state.update_data(edit_mode=1)
    func_name = key
    func_name_with_input = func_name + "_input"

    # Получаем ссылку на функцию с использованием имени и вызываем ее
    function_to_call = globals()[func_name_with_input]
    await function_to_call(message, state)
